package org.liyf.tools;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeType;
import com.google.googlejavaformat.java.Formatter;
import com.google.googlejavaformat.java.FormatterException;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Iterator;
import java.util.Map;

public class Json2pojo {
  private static final String DEMO_FILEPATH = "/config.json";
  private static final String DEMO_TARGETPATH = "src/main/java/";

  public static void main(String[] args) throws IOException, FormatterException {
    String filepath = DEMO_FILEPATH;
    String targetpath = DEMO_TARGETPATH;
    String packageName = "";
    String jsonStr = IOUtils.resourceToString(filepath, StandardCharsets.UTF_8);
    if (args.length > 0){
      filepath = args[0];
      jsonStr = FileUtils.readFileToString(new File(filepath), StandardCharsets.UTF_8);
    }
    if (args.length > 1){
      targetpath = args[1];
    }
    if (args.length > 2) {
      packageName = args[2];
    }

    ObjectMapper mapper = new ObjectMapper();


    JsonNode rootNode = mapper.readTree(jsonStr);
    String filename = FilenameUtils.getBaseName(filepath);
    String s = build(rootNode, upperFirst(filename), true);
    if (packageName.length() > 0) {
      s = String.format("package %s;", packageName) + s;
    }

    Path path = Path.of(targetpath);
    if (!path.toFile().exists()) {
      Files.createDirectories(path);
    }

    String formattedSource = new Formatter().formatSource(s);
    IOUtils.write(formattedSource, new FileOutputStream(targetpath + upperFirst(filename) + ".java"), StandardCharsets.UTF_8);
  }

  static String publicClassTempate = "public class ${className:upperFirst} {$content}";
  static String staticClassTempate = "static class ${className:upperFirst} {$content}";
  static String fieldTempate = "private $type $fieldName;";
  static String setMethodTemplate = "public void set${fieldName:upperFirst} ($type $fieldName) {this.$fieldName = $fieldName;}";
  static String getMethodTemplate = "public $type get${fieldName:upperFirst}() {return this.$fieldName;}";

  public static String build(JsonNode jsonNode,String className, boolean isRoot) {

    String classTemplate;
    if (isRoot) {
      classTemplate = publicClassTempate;
    } else {
      classTemplate = staticClassTempate;
    }
    classTemplate = classTemplate.replace("${className:upperFirst}", upperFirst(className));

    StringBuilder sb = new StringBuilder();
    for (Iterator<Map.Entry<String, JsonNode>> it = jsonNode.fields(); it.hasNext(); ) {
      Map.Entry<String, JsonNode> elt = it.next();
      JsonNode eltNode = elt.getValue();

      String fieldName = elt.getKey();
      String type = convertType(eltNode.getNodeType());

      if (eltNode.getNodeType() == JsonNodeType.OBJECT) {
        type = upperFirst(fieldName);
        sb.append(build(eltNode, fieldName, false));
      } else if (eltNode.getNodeType() == JsonNodeType.ARRAY){
        ArrayNode arrayNode = (ArrayNode) eltNode;
        String arrayType = convertType(arrayNode.get(0).getNodeType());
        if (arrayType.length() == 0){
          throw new RuntimeException("数组暂时只支持基础类型");
        }
        type = arrayType + "[]";
      }
      sb.append(fieldTempate
              .replace("$type",type)
              .replace("$fieldName", fieldName)).append("\n")
              .append(getMethodTemplate
                      .replace("$type",type)
                      .replace("$fieldName", fieldName)
                      .replace("${fieldName:upperFirst}", upperFirst(fieldName))).append("\n")
              .append(setMethodTemplate
                      .replace("$type",type)
                      .replace("$fieldName", fieldName)
                      .replace("${fieldName:upperFirst}", upperFirst(fieldName))).append("\n");
    }
    classTemplate = classTemplate.replace("$content", sb.toString());
    return classTemplate;
  }

  public static String upperFirst(String oldStr) {
    return oldStr.substring(0, 1).toUpperCase() + oldStr.substring(1);
  }

  public static String convertType(JsonNodeType nodeType) {
    switch (nodeType) {
      case NUMBER:
        return Integer.class.getSimpleName();
      case STRING:
        return String.class.getSimpleName();
      case NULL:
        throw new RuntimeException("需要非null字段来定义类型");
      default:
        return "";
    }
  }
}
